package com.dd.seckill.single.service.task;

import com.dd.seckill.single.config.cache.caffeine.SeckillGoodsCache;
import com.dd.seckill.single.config.redis.RedisKeyConstants;
import com.dd.seckill.single.mapper.SeckillGoodsMapper;
import com.dd.seckill.single.pojo.DO.SeckillGoods;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;

/**
 * @Author liuxianmeng
 * @CreateTime 2025/9/8 0:56
 * @Description 秒杀商品信息预热
 */
@Slf4j
@Component
public class SeckillGoodsWarmUp implements ApplicationRunner {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Value("${seckill.warmup.enabled}")
    private boolean warmupEnabled;

    @Value("${seckill.warmup.goods-id}")
    private long warmupGoodsId;

    @Autowired
    private SeckillGoodsMapper seckillGoodsMapper;

    @Override
    public void run(ApplicationArguments args) {
        if (warmupEnabled) {
            log.info("开始执行秒杀商品预热任务..." + LocalDateTime.now());
            executeWarmUp();
            log.info("预热任务执行完成！");
        } else {
            log.info("秒杀商品预热任务已禁用");
        }
    }

    private void executeWarmUp() {
        try {
            warmUpSeckillGoods();
        } catch (Exception e) {
            log.error("预热任务执行失败: ", e);
        }
    }

    /**
     * 预热秒杀商品数据到Redis
     */
    private void warmUpSeckillGoods() {
        String sql = "SELECT id, goods_name, original_price, seckill_price, " +
                "initial_stock, available_stock, start_time, end_time " +
                "FROM seckill_goods " +
                "WHERE available_stock > 0 " +
                "AND id = ?";

        try {
            List<SeckillGoods> goodsList = jdbcTemplate.query(sql, new Object[]{warmupGoodsId}, (rs, rowNum) -> {
                SeckillGoods goods = new SeckillGoods();
                goods.setId(rs.getLong("id"));
                goods.setGoodsName(rs.getString("goods_name"));
                goods.setOriginalPrice(BigDecimal.valueOf(rs.getDouble("original_price")));
                goods.setSeckillPrice(BigDecimal.valueOf(rs.getDouble("seckill_price")));
                goods.setAvailableStock(rs.getInt("available_stock"));
                goods.setStartTime(rs.getTimestamp("start_time").toLocalDateTime());
                goods.setEndTime(rs.getTimestamp("end_time").toLocalDateTime());
                return goods;
            });

            int processedCount = 0;

            for (SeckillGoods good : goodsList) {
                long goodsId = good.getId();
                String goodsKey = RedisKeyConstants.GOODS_KEY_PREFIX + goodsId;
                String stockKey = RedisKeyConstants.STOCK_KEY_PREFIX + goodsId;
                String successUserSetKey = RedisKeyConstants.USER_SET_KEY_PREFIX + goodsId;

                // 【方案1】存储商品信息到Redis Hash
                //Map<String, Object> goodsInfo = new HashMap<>();
                //goodsInfo.put("id", String.valueOf(goodsId));
                //goodsInfo.put("goodsName", goods.getGoodsName());
                //goodsInfo.put("seckillPrice", String.valueOf(goods.getSeckillPrice()));
                //goodsInfo.put("startTime", goods.getStartTime().toString());
                //goodsInfo.put("endTime", goods.getEndTime().toString());
                //redisTemplate.opsForHash().putAll(goodsKey, goodsInfo);
                //// 存储库存到String
                redisTemplate.opsForValue().set(stockKey, good.getAvailableStock());
                //// 清空并初始化用户集合
                redisTemplate.delete(successUserSetKey);

                // 【方案2】存储商品信息到Caffeine本地缓存
                SeckillGoodsCache.seckillGoodsCache.put(goodsId, good);
                log.info("Caffeine缓存加载成功：SeckillGoodsCache.seckillGoodsCache.get({}) = {}",
                        goodsId, SeckillGoodsCache.seckillGoodsCache.get(goodsId, new Function<Long, SeckillGoods>() {
                            @Override
                            public SeckillGoods apply(Long aLong) {
                                return seckillGoodsMapper.selectById(aLong);
                            }
                        }));

                processedCount++;
                log.info("✓ 预热成功: ID={}, 名称={}, 库存={}", goodsId, good.getGoodsName(), good.getAvailableStock());
            }

            log.info("共处理 {} 个商品", processedCount);

        } catch (Exception e) {
            log.error("预热商品数据失败: ", e);
        }
    }
}